package com.gugee.common.thread;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.gugee.common.exception.GugeeException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.*;

@Component
@Slf4j
public class ScheduleTemplate {
    private final Map<String, ThreadPoolExecutor> nameThreadMap = new ConcurrentHashMap<>();
    private final Map<String, ForkJoinPool> nameForkJoinPool = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        log.info("ScheduleTemplate  init");
        nameForkJoinPool.put("ForkJoinPool-common", ForkJoinPool.commonPool());
    }
    @SneakyThrows
    public void run(String name, int parallelism, int waitMillis, Runnable runnable) {
        long end = System.currentTimeMillis()+10*waitMillis;
        while (System.currentTimeMillis()>end) {
            ThreadPoolExecutor executor=nameThreadMap.get(name);
            if(executor==null){
                synchronized (nameThreadMap) {
                    executor = nameThreadMap.computeIfAbsent(name, k ->ThreadPoolUtils.namedFixedThreadPool(parallelism, name));
                }
            }
            if (executor!=null && executor.getActiveCount() < parallelism) {
                executor.execute(runnable);
                return;
            }
            Thread.sleep(waitMillis);
        }
        throw new GugeeException(nameThreadMap+"is busy","pools is full");
    }

    @SneakyThrows
    public ThreadPoolExecutor getCustomThreadPool(String name, int corePoolSize,
                                               int maximumPoolSize,
                                               long keepAliveTime, int queueSize,
                                               TimeUnit unit) {

        ThreadPoolExecutor executor = nameThreadMap.get(name);
        if (executor != null) {
            return executor;
        }
        synchronized (nameThreadMap) {
            return nameThreadMap.computeIfAbsent(name,k->{
                BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(queueSize);
                ThreadFactory guavaThreadFactory = new ThreadFactoryBuilder().setNameFormat(name+"-pl-").build();
                return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,guavaThreadFactory);
            });
        }
    }


    public Map<String, ThreadPoolExecutor> getNameThreadMap() {
        return nameThreadMap;
    }

    public Map<String, ForkJoinPool> getNameForkJoinPool() {
        return nameForkJoinPool;
    }

    public void attach(String key, ForkJoinPool forkJoinPool) {
        this.nameForkJoinPool.put(key, forkJoinPool);
    }
}
